import sys
import os
# os.path.dirname(path)——返回文件路径
# os.path.abspath()——获得绝对路径。
curPath = os.path.abspath(os.path.dirname(__file__)) #storages文件夹的绝对路径
# os.path.split(path)——返回路径的目录和文件名，即将目录和文件名分开，而不是一个整体。此处只是把前后两部分分开而已。就是找最后一个’/’
rootPath = os.path.split(curPath)[0] #得到 storages文件夹 父文件夹proxypool 的绝对路径
# print(rootPath)
# 错：sys.path.append(rootPath)
sys.path.append(os.path.split(rootPath)[0]) #要添加proxypool文件夹的父文件ProxyPool夹，才能找到 "module proxypool"

import redis
from proxypool.exceptions import PoolEmptyException
from proxypool.schemas.proxy import Proxy
from proxypool.setting import REDIS_HOST, REDIS_PORT, REDIS_DB, REDIS_PASSWORD, REDIS_KEY, PROXY_SCORE_MAX, PROXY_SCORE_MIN, \
    PROXY_SCORE_INIT
from random import choice
from typing import List
from loguru import logger
from proxypool.utils.proxy import is_valid_proxy, convert_proxy_or_proxies


# REDIS_CLIENT_VERSION = redis.__version__
REDIS_CLIENT_VERSION = '3.3.11'
IS_REDIS_VERSION_2 = REDIS_CLIENT_VERSION.startswith('2.')


class RedisClient(object):
    """
    用来操作Redis的有序集合，定义了一些方法来对集合中的元素进行处理
    redis connection client of proxypool
    """
    
    def __init__(self, host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DB, password=REDIS_PASSWORD, **kwargs):
        """
        初始化
        init redis client
        :param host: redis host
        :param port: redis port
        :param password: redis password
        """
        self.db = redis.StrictRedis(host=host, port=port, db=db, password=password, decode_responses=True, **kwargs)

    # 添加代理，设置分数为初始分数——10
    def add(self, proxy: Proxy, score=PROXY_SCORE_INIT) -> int:
        """
        add proxy and set it to init score
        :param proxy: proxy, ip:port, like 8.8.8.8:88
        :param score: int score
        :return: result
        """
        if not is_valid_proxy(f'{proxy.host}:{proxy.port}'):
            logger.info(f'invalid proxy {proxy}, throw it')
            return
        if not self.exists(proxy):
            if IS_REDIS_VERSION_2:
                return self.db.zadd(REDIS_KEY, score, proxy)

            # 他妈妈的！！！就是这里除了问题！！！（2020-03-03）
            # return self.db.zadd(REDIS_KEY, {proxy: score})
            # return self.db.zadd(REDIS_KEY, {str(proxy): score})
            return self.db.zadd(REDIS_KEY, {proxy.string(): score})

    def random(self) -> Proxy:
        """
        随机获取有效代理，首先尝试获取最高分数代理，如果最高分数不存在，则按照排名获取，否则异常
        get random proxy
        firstly try to get proxy with max score
        if not exists, try to get proxy by rank
        if not exists, raise error
        :return: proxy, like 8.8.8.8:8  随机代理
        """
        # try to get proxy with max score
        proxies = self.db.zrangebyscore(REDIS_KEY, PROXY_SCORE_MAX, PROXY_SCORE_MAX)
        if len(proxies):
            return convert_proxy_or_proxies(choice(proxies))
        # else get proxy by rank
        proxies = self.db.zrevrange(REDIS_KEY, PROXY_SCORE_MIN, PROXY_SCORE_MAX) #最后一个参数为选取的数量，相当于Stata中的 list PROXY_SCORE_MAX
        if len(proxies):
            return convert_proxy_or_proxies(choice(proxies))
        # else raise error
        raise PoolEmptyException
    
    def decrease(self, proxy: Proxy) -> int:
        """
        检测在代理检测无效时，代理值减一分，分数小于最小值，则代理删除
        decrease score of proxy, if small than PROXY_SCORE_MIN, delete it
        :param proxy: proxy 代理
        :return: new score 修改后的代理分数
        """
        score = self.db.zscore(REDIS_KEY, proxy.string())
        # current score is larger than PROXY_SCORE_MIN
        if score and score > PROXY_SCORE_MIN:
            logger.info(f'{proxy.string()} current score {score}, decrease 1')
            if IS_REDIS_VERSION_2:
                return self.db.zincrby(REDIS_KEY, proxy.string(), -1)
            return self.db.zincrby(REDIS_KEY, -1, proxy.string())
        # otherwise delete proxy
        else:
            logger.info(f'{proxy.string()} current score {score}, remove')
            # 2020-03-04修改，原：return self.db.zrem(REDIS_KEY, proxy)
            return self.db.zrem(REDIS_KEY, str(proxy))

    # 判断代理是否存在在集合中
    def exists(self, proxy: Proxy) -> bool:
        """
        if proxy exists
        :param proxy: proxy 代理
        :return: if exists, bool    是否存在
        """
        return not self.db.zscore(REDIS_KEY, proxy.string()) is None

    # 将代理的分数设置为 MAX_SCORE，即100，也就是当代理有效时的设置
    def max(self, proxy: Proxy) -> int:
        """
        set proxy to max score
        :param proxy: proxy 代理
        :return: new score  设置结果
        """
        logger.info(f'{proxy.string()} is valid, set to {PROXY_SCORE_MAX}')
        if IS_REDIS_VERSION_2:
            return self.db.zadd(REDIS_KEY, PROXY_SCORE_MAX, proxy.string())
        return self.db.zadd(REDIS_KEY, {proxy.string(): PROXY_SCORE_MAX})

    # 获取数量（返回当前集合的元素个数）
    def count(self) -> int:
        """
        get count of proxies
        :return: count, int 数量
        """
        return self.db.zcard(REDIS_KEY)

    # 获取全部代理（返回所有的代理，以供检测使用）
    def all(self) -> List[Proxy]:
        """
        get all proxies
        :return: list of proxies 全部代理列表
        """
        return convert_proxy_or_proxies(self.db.zrangebyscore(REDIS_KEY, PROXY_SCORE_MIN, PROXY_SCORE_MAX))
    
    def batch(self, start, end) -> List[Proxy]:
        """
        get batch of proxies
        :param start: start index
        :param end: end index
        :return: list of proxies
        """
        return convert_proxy_or_proxies(self.db.zrevrange(REDIS_KEY, start, end - 1))


if __name__ == '__main__':
    conn = RedisClient()
    result = conn.random()
    print(result)
